Skip to content

[InstCombine] Fold bitcast [x,...,x] to iN to mul x, C - #185907

Open
Camsyn wants to merge 9 commits into
llvm:mainfrom
Camsyn:fix-185694
Open

[InstCombine] Fold bitcast [x,...,x] to iN to mul x, C#185907
Camsyn wants to merge 9 commits into
llvm:mainfrom
Camsyn:fix-185694

Conversation

@Camsyn

@Camsyn Camsyn commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Fold bitcast [x,x,...,x,x] to iN to mul x, C as a splat-vec can be treated as a multiplication of one of its elements.

Mathematical model of splat-vec to bitcast

$x$: an integer with bit-width $W$
$y$: the dst integer with bit-width $N$, which is casted from a vector with $N/W$ elements.

$$ x \to_{splat} [x,\dots,x]_{N/W} \to_{\text{bitcast}} y \quad \implies \quad y=\sum_{i=0}^{N/W-1}x \cdot 2^{Wi}=x \cdot \sum_{i=0}^{N/W-1}2^{Wi} =Cx, \quad C=\frac{2^{N}-1}{2^W-1} $$

Example

$$ x: \text{i1}; N=4 \implies C=1+2+4+8=15 \implies y = [x,x,x,x] = 15x $$


Fixes #185694
Alive2: https://alive2.llvm.org/ce/z/ogrYtR
Godbolt: https://godbolt.org/z/cYd8qfb87
IR Diff: dtcxzyw/llvm-opt-benchmark-nightly#100
Negligible CompTime Impact: dtcxzyw/llvm-opt-benchmark-nightly#114

Comment on lines 116 to 118

@Camsyn Camsyn Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another missed optimization found: alive2 proof. I will fix it as a follow-up.

Should be fixed by #186347.

@Camsyn
Camsyn requested a review from dtcxzyw March 11, 2026 16:58
@Camsyn
Camsyn marked this pull request as ready for review March 11, 2026 17:00
@Camsyn
Camsyn requested a review from nikic as a code owner March 11, 2026 17:00
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Mar 11, 2026
@llvmbot

llvmbot commented Mar 11, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-llvm-transforms

Author: Kunqiu Chen (Camsyn)

Changes

Fold bitcast [x,x,...,x,x] to iN to mul x, C as a splat-vec can be treated as a multiplication of one of its elements.

Mathematical model of splat-vec to bitcast

$x$: an integer with bit-width $W$
$y$: the dst integer with bit-width $N$, which is casted from a vector with $N/W$ elements.

$$
x \to_{splat} [x,\dots,x]{N/W} \to{\text{bitcast}} y
\quad \implies \quad
y=\sum_{i=0}^{N/W-1}x \cdot 2^{Wi}=x \cdot \sum_{i=0}^{N/W-1}2^{Wi} =Cx, \quad C=\frac{2^{N}-1}{2^W-1}
$$

Example

$$
x: \text{i1}; N=4 \implies C=1+2+4+8=15 \implies y = [x,x,x,x] = 15x
$$


Fixes #185694
Alive2: https://alive2.llvm.org/ce/z/ogrYtR
Godbolt: https://godbolt.org/z/cYd8qfb87
IR Diff: dtcxzyw/llvm-opt-benchmark#3554


Full diff: https://github.com/llvm/llvm-project/pull/185907.diff

4 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp (+61-2)
  • (added) llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll (+101)
  • (modified) llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll (+4-6)
  • (modified) llvm/test/Transforms/InstCombine/icmp-bc-vec.ll (+4-6)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 0cd2c09726a2d..cdd34d90c0a4a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -3083,6 +3083,57 @@ static Value *foldCopySignIdioms(BitCastInst &CI,
   return Builder.CreateCopySign(Builder.CreateBitCast(Y, FTy), X);
 }
 
+// bitcast (shuf X, Y, splat_mask) to iN --> (zext x) * C
+// where x is the splatted integer source element with bitwidth W and
+//       C = 1 + 2^W + 2^(2W) + ... = (2^N - 1)/(2^W - 1)
+// E.g.,
+//   x: i1, y = bitcast [x, x, x, x] --> y = x * 15
+//   x: i8, y = bitcast [x, x, x, x] --> y = x * 16843009
+static Value *foldSplatShuffleToMul(ShuffleVectorInst *Shuf, IntegerType *DstTy,
+                                    InstCombiner::BuilderTy &Builder) {
+  auto *ShufTy = dyn_cast<FixedVectorType>(Shuf->getType());
+  if (!ShufTy)
+    return nullptr;
+  auto *EltTy = dyn_cast<IntegerType>(ShufTy->getElementType());
+  // Restrict this fold to integer splats. Reinterpreting a non-integer splat
+  // element as an integer and then multiplying by C is algebraically sound, but
+  // llvm-mca shows that it can generate worse code than keeping the
+  // splat-vector bitcast form.
+  if (!EltTy)
+    return nullptr;
+  ArrayRef<int> Mask = Shuf->getShuffleMask();
+
+  // Check if this is a splat-shuffle with a valid index
+  if (!all_equal(Mask) || Mask[0] == PoisonMaskElem)
+    return nullptr;
+
+  unsigned DstWidth = DstTy->getBitWidth();
+  assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() &&
+         "bitcast width mismatch");
+
+  unsigned SplatIndex = static_cast<unsigned>(Mask[0]);
+  Value *SplatSource = Shuf->getOperand(0);
+  unsigned NumElts =
+      cast<FixedVectorType>(SplatSource->getType())->getNumElements();
+  if (SplatIndex >= NumElts) {
+    SplatSource = Shuf->getOperand(1);
+    SplatIndex -= NumElts;
+  }
+
+  assert(SplatIndex <
+             cast<FixedVectorType>(SplatSource->getType())->getNumElements() &&
+         "splat index must be within the selected shuffle source");
+
+  // bitcast (splat x) to integer is:
+  //   y = x * C, where C = 1 + 2^W + 2^(2W) + ...
+  // and W is the source element width.
+  Value *Splat =
+      Builder.CreateExtractElement(SplatSource, Builder.getInt64(SplatIndex));
+  APInt MulC = APInt::getSplat(DstWidth, APInt(EltTy->getBitWidth(), 1));
+  Value *WideSplat = Builder.CreateZExt(Splat, DstTy);
+  return BinaryOperator::CreateMul(WideSplat, ConstantInt::get(DstTy, MulC));
+}
+
 Instruction *InstCombinerImpl::visitBitCast(BitCastInst &CI) {
   // If the operands are integer typed then apply the integer transforms,
   // otherwise just apply the common ones.
@@ -3161,8 +3212,16 @@ Instruction *InstCombinerImpl::visitBitCast(BitCastInst &CI) {
   }
 
   if (auto *Shuf = dyn_cast<ShuffleVectorInst>(Src)) {
-    // Okay, we have (bitcast (shuffle ..)).  Check to see if this is
-    // a bitcast to a vector with the same # elts.
+    // Okay, we have (bitcast (shuffle ..)).
+
+    // Check to see if the bitcast to iN is only user of a splat-shuffle.
+    // If so, try to fold `bitcast [x, ..., x] to iN` into `(zext x) * C`
+    if (auto *DstIntTy = dyn_cast<IntegerType>(DestTy);
+        DstIntTy && Shuf->hasOneUser())
+      if (Value *V = foldSplatShuffleToMul(Shuf, DstIntTy, Builder))
+        return cast<Instruction>(V);
+
+    // Check to see if this is a bitcast to a vector with the same # elts.
     Value *ShufOp0 = Shuf->getOperand(0);
     Value *ShufOp1 = Shuf->getOperand(1);
     auto ShufElts = cast<VectorType>(Shuf->getType())->getElementCount();
diff --git a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll
new file mode 100644
index 0000000000000..15d2de7f381ce
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll
@@ -0,0 +1,101 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @issue185694_i1(i64 %arg0, i64 %arg1) {
+; CHECK-LABEL: define i32 @issue185694_i1(
+; CHECK-SAME: i64 [[ARG0:%.*]], i64 [[ARG1:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[ARG1]], [[ARG0]]
+; CHECK-NEXT:    [[NEG:%.*]] = sext i1 [[CMP]] to i4
+; CHECK-NEXT:    [[EXT:%.*]] = zext i4 [[NEG]] to i32
+; CHECK-NEXT:    ret i32 [[EXT]]
+;
+  %cmp = icmp eq i64 %arg1, %arg0
+  %ins = insertelement <4 x i1> poison, i1 %cmp, i64 0
+  %splat = shufflevector <4 x i1> %ins, <4 x i1> poison, <4 x i32> zeroinitializer
+  %bc = bitcast <4 x i1> %splat to i4
+  %ext = zext i4 %bc to i32
+  ret i32 %ext
+}
+
+define i32 @splat_i8_nonzero_lane(i8 %x) {
+; CHECK-LABEL: define i32 @splat_i8_nonzero_lane(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[X]] to i32
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i32 [[TMP1]], 16843009
+; CHECK-NEXT:    ret i32 [[MUL]]
+;
+  %ins = insertelement <4 x i8> poison, i8 %x, i64 2
+  %splat = shufflevector <4 x i8> %ins, <4 x i8> poison, <4 x i32> <i32 2, i32 2, i32 2, i32 2>
+  %bc = bitcast <4 x i8> %splat to i32
+  ret i32 %bc
+}
+
+define i64 @zext_splat_i8_to_i64(i8 %x) {
+; CHECK-LABEL: define i64 @zext_splat_i8_to_i64(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[X]] to i16
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i16 [[TMP1]], 257
+; CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[MUL]] to i64
+; CHECK-NEXT:    ret i64 [[EXT]]
+;
+  %ins = insertelement <2 x i8> poison, i8 %x, i64 0
+  %splat = shufflevector <2 x i8> %ins, <2 x i8> poison, <2 x i32> zeroinitializer
+  %bc = bitcast <2 x i8> %splat to i16
+  %ext = zext i16 %bc to i64
+  ret i64 %ext
+}
+
+define i64 @splat_float(float %x) {
+; CHECK-LABEL: define i64 @splat_float(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = insertelement <2 x float> poison, float [[X]], i64 0
+; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <2 x float> [[TMP1]], <2 x float> poison, <2 x i32> zeroinitializer
+; CHECK-NEXT:    [[BC:%.*]] = bitcast <2 x float> [[SPLAT]] to i64
+; CHECK-NEXT:    ret i64 [[BC]]
+;
+  %ins = insertelement <2 x float> poison, float %x, i64 1
+  %splat = shufflevector <2 x float> %ins, <2 x float> poison, <2 x i32> <i32 1, i32 1>
+  %bc = bitcast <2 x float> %splat to i64
+  ret i64 %bc
+}
+
+define i128 @splat_float_to_i128(float %x) {
+; CHECK-LABEL: define i128 @splat_float_to_i128(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = insertelement <4 x float> poison, float [[X]], i64 0
+; CHECK-NEXT:    [[SPLAT:%.*]] = shufflevector <4 x float> [[TMP1]], <4 x float> poison, <4 x i32> zeroinitializer
+; CHECK-NEXT:    [[BC:%.*]] = bitcast <4 x float> [[SPLAT]] to i128
+; CHECK-NEXT:    ret i128 [[BC]]
+;
+  %ins = insertelement <4 x float> poison, float %x, i64 3
+  %splat = shufflevector <4 x float> %ins, <4 x float> poison,
+  <4 x i32> <i32 3, i32 3, i32 3, i32 3>
+  %bc = bitcast <4 x float> %splat to i128
+  ret i128 %bc
+}
+
+define i128 @splat_i32_to_i128(i32 %x) {
+; CHECK-LABEL: define i128 @splat_i32_to_i128(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[X]] to i128
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i128 [[TMP1]], 79228162532711081671548469249
+; CHECK-NEXT:    ret i128 [[MUL]]
+;
+  %ins = insertelement <4 x i32> poison, i32 %x, i64 1
+  %splat = shufflevector <4 x i32> %ins, <4 x i32> poison,
+  <4 x i32> <i32 1, i32 1, i32 1, i32 1>
+  %bc = bitcast <4 x i32> %splat to i128
+  ret i128 %bc
+}
+
+define i32 @nonsplat_shuffle(<4 x i8> %x) {
+; CHECK-LABEL: define i32 @nonsplat_shuffle(
+; CHECK-SAME: <4 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[SHUF:%.*]] = shufflevector <4 x i8> [[X]], <4 x i8> poison, <4 x i32> <i32 0, i32 1, i32 0, i32 1>
+; CHECK-NEXT:    [[BC:%.*]] = bitcast <4 x i8> [[SHUF]] to i32
+; CHECK-NEXT:    ret i32 [[BC]]
+;
+  %shuf = shufflevector <4 x i8> %x, <4 x i8> poison, <4 x i32> <i32 0, i32 1, i32 0, i32 1>
+  %bc = bitcast <4 x i8> %shuf to i32
+  ret i32 %bc
+}
diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll
index 2d4e4920d551c..2341ad5df05b6 100644
--- a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll
@@ -88,11 +88,7 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) {
 ; Make sure we don't try to fold if the compared-to constant isn't a splatted value
 define i1 @test_i8_nopattern(i8 %val) {
 ; CHECK-LABEL: @test_i8_nopattern(
-; CHECK-NEXT:    [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0
-; CHECK-NEXT:    [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32
-; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647
-; CHECK-NEXT:    ret i1 [[COND]]
+; CHECK-NEXT:    ret i1 false
 ;
   %insvec = insertelement <4 x i8> poison, i8 %val, i32 0
   %vec = shufflevector <4 x i8> %insvec, <4 x i8> poison, <4 x i32> zeroinitializer
@@ -117,7 +113,9 @@ define i1 @test_i8_ult_pattern(i8 %val) {
 define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) {
 ; CHECK-LABEL: @extending_shuffle_with_weird_types(
 ; CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = zext i9 [[TMP1]] to i27
+; CHECK-NEXT:    [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %splat = shufflevector <2 x i9> %v, <2 x i9> poison, <3 x i32> zeroinitializer
diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll
index 9369d5564c3a2..92de9a2646cbb 100644
--- a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll
@@ -88,11 +88,7 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) {
 ; Make sure we don't try to fold if the compared-to constant isn't a splatted value
 define i1 @test_i8_nopattern(i8 %val) {
 ; CHECK-LABEL: @test_i8_nopattern(
-; CHECK-NEXT:    [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0
-; CHECK-NEXT:    [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer
-; CHECK-NEXT:    [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32
-; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647
-; CHECK-NEXT:    ret i1 [[COND]]
+; CHECK-NEXT:    ret i1 false
 ;
   %insvec = insertelement <4 x i8> undef, i8 %val, i32 0
   %vec = shufflevector <4 x i8> %insvec, <4 x i8> undef, <4 x i32> zeroinitializer
@@ -117,7 +113,9 @@ define i1 @test_i8_ult_pattern(i8 %val) {
 define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) {
 ; CHECK-LABEL: @extending_shuffle_with_weird_types(
 ; CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = zext i9 [[TMP1]] to i27
+; CHECK-NEXT:    [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %splat = shufflevector <2 x i9> %v, <2 x i9> undef, <3 x i32> zeroinitializer

Comment on lines 116 to 118

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed by #186347.

Comment thread llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp Outdated
Comment thread llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp Outdated
Comment thread llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp Outdated
Comment thread llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp Outdated
Comment thread llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp Outdated
return nullptr;

unsigned DstWidth = DstTy->getBitWidth();
assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be less beneficial when the dest type is so large that it needs to be legalized in the backend. Can we be more conservative by checking DL.fitsInLegalInteger?

@Camsyn Camsyn Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a related guard as follows:

  if (!Shuf.getDataLayout().fitsInLegalInteger(DstWidth))
    return nullptr;

However, Godbolt shows that i128 mul is better than splat [4 × i32] bitcast on x86_64, while i256 mul is worse than splat [8 × i32] bitcast. What's more, transforming splat vec bitcast as integer mul is more beneficial for the following optimizations.

Accordingly, should we relax this restriction to DL.fitsInLegalInteger((BW + 1)/ 2)? I.e.,

  if (!Shuf.getDataLayout().fitsInLegalInteger((DstWidth + 1) / 2))
    return nullptr;

@dtcxzyw , do you have any suggestions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered using vectorcombine instead and making this cost driven?

@Camsyn Camsyn Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out.

As vectorcombine uses TTI to estimate the cost, I find that the cost estimated by TTI mismatches what llvm-mca says.

E.g., on x86_64,

  • TTI says (iN)float_val * C is better than bitcast splat-float-vec to iN(cost: 3 vs 4), which is against llvm-mca.
  • TTI says (i256)val * C is better than bitcast splat-vec to i256 (cost: 8 vs 16), which is against llvm-mca.

@RKSimon, do you think about which side we should believe? If only relying on TTI is okay, I will move this optimization to vector-combine.

@dtcxzyw
dtcxzyw requested a review from RKSimon April 5, 2026 17:59
@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 133125 tests passed
  • 3066 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Apr 7, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 193163 tests passed
  • 4996 tests skipped

✅ The build succeeded and all tests passed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need this triple?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. In current implementation, DL.fitsInLegalInteger requires this triple.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in which case you should probably move this into instcombine/x86 subdir - but I still wonder if you should be doing this in vectorcombine

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As vectorcombine uses TTI to estimate the cost, I find that the cost estimated by TTI mismatches what llvm-mca says.

E.g., on x86_64,

  • TTI says (iN)float_val * C is better than bitcast splat-float-vec to iN(cost: 3 vs 4), which is against llvm-mca.
  • TTI says (i256)val * C is better than bitcast splat-vec to i256 (cost: 8 vs 16), which is against llvm-mca.

@RKSimon, do you think about which side we should believe? If only relying on TTI is okay, I will move this optimization to vector-combine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missed optimization: simplify splat-vector bitcast to scalar multiplication/masking

4 participants